package com.ruoyi.common.utils.baidu;

/**
 * @author: jkk
 * @create: 2025/3/19-20:10
 * @Description:
 */
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ruoyi.common.utils.baidu.entity.*;

import java.lang.reflect.Type;
import java.util.List;

public class GeoUtils {

    // 地球半径
    private static final double EARTH_RADIUS = 6370996.81;

    /**
     * 判断点是否在矩形内
     *
     * @param point  点对象
     * @param bounds 矩形边界对象
     * @return 点在矩形内返回 true，否则返回 false
     */
    public static boolean isPointInRect(Point point, Bounds bounds) {
        if (point == null || bounds == null) {
            return false;
        }
        Point sw = bounds.getSouthWest(); // 西南角点
        Point ne = bounds.getNorthEast(); // 东北角点
        return point.getLng() >= sw.getLng() &&
                point.getLng() <= ne.getLng() &&
                point.getLat() >= sw.getLat() &&
                point.getLat() <= ne.getLat();
    }

    /**
     * 判断点是否在圆形内
     *
     * @param point  点对象
     * @param circle 圆形对象
     * @return 点在圆形内返回 true，否则返回 false
     */
    public static boolean isPointInCircle(Point point, Circle circle) {
        if (point == null || circle == null) {
            return false;
        }
        Point center = circle.getCenter();
        double radius = circle.getRadius();
        double distance = getDistance(point, center);
        return distance <= radius;
    }

    /**
     * 判断点是否在折线上
     *
     * @param point    点对象
     * @param polyline 折线对象
     * @return 点在折线上返回 true，否则返回 false
     */
    public static boolean isPointOnPolyline(Point point, Polyline polyline) {
        if (point == null || polyline == null) {
            return false;
        }
        Bounds lineBounds = polyline.getBounds();
        if (!isPointInRect(point, lineBounds)) {
            return false;
        }
        List<Point> pts = polyline.getPath();
        for (int i = 0; i < pts.size() - 1; i++) {
            Point curPt = pts.get(i);
            Point nextPt = pts.get(i + 1);
            if (point.getLng() >= Math.min(curPt.getLng(), nextPt.getLng()) &&
                    point.getLng() <= Math.max(curPt.getLng(), nextPt.getLng()) &&
                    point.getLat() >= Math.min(curPt.getLat(), nextPt.getLat()) &&
                    point.getLat() <= Math.max(curPt.getLat(), nextPt.getLat())) {
                double precision = (curPt.getLng() - point.getLng()) * (nextPt.getLat() - point.getLat()) -
                        (nextPt.getLng() - point.getLng()) * (curPt.getLat() - point.getLat());
                if (precision < 2e-9 && precision > -2e-9) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断点是否在多边形内
     *
     * @param point   点对象
     * @param polygon 多边形对象
     * @return 点在多边形内返回 true，否则返回 false
     */
    public static boolean isPointInPolygon(Point point, Polygon polygon) {
        if (point == null || polygon == null) {
            return false;
        }
        Bounds polygonBounds = polygon.getBounds();
        if (!isPointInRect(point, polygonBounds)) {
            return false;
        }
        List<Point> pts = polygon.getPath();
        int N = pts.size();
        boolean boundOrVertex = true;
        int intersectCount = 0;
        double precision = 2e-10;
        Point p1, p2;
        Point p = point;
        p1 = pts.get(0);
        for (int i = 1; i <= N; i++) {
            if (p.equals(p1)) {
                return boundOrVertex;
            }
            p2 = pts.get(i % N);
            if (p.getLat() < Math.min(p1.getLat(), p2.getLat()) ||
                    p.getLat() > Math.max(p1.getLat(), p2.getLat())) {
                p1 = p2;
                continue;
            }
            if (p.getLat() > Math.min(p1.getLat(), p2.getLat()) &&
                    p.getLat() < Math.max(p1.getLat(), p2.getLat())) {
                if (p.getLng() <= Math.max(p1.getLng(), p2.getLng())) {
                    if (p1.getLat() == p2.getLat() && p.getLng() >= Math.min(p1.getLng(), p2.getLng())) {
                        return boundOrVertex;
                    }
                    if (p1.getLng() == p2.getLng()) {
                        if (p1.getLng() == p.getLng()) {
                            return boundOrVertex;
                        } else {
                            ++intersectCount;
                        }
                    } else {
                        double xinters = ((p.getLat() - p1.getLat()) * (p2.getLng() - p1.getLng())) /
                                (p2.getLat() - p1.getLat()) + p1.getLng();
                        if (Math.abs(p.getLng() - xinters) < precision) {
                            return boundOrVertex;
                        }
                        if (p.getLng() < xinters) {
                            ++intersectCount;
                        }
                    }
                }
            } else {
                if (p.getLat() == p2.getLat() && p.getLng() <= p2.getLng())
                     {
                    Point p3 = pts.get((i + 1) % N);
                    if (p.getLat() >= Math.min(p1.getLat(), p3.getLat()) &&
                            p.getLat() <= Math.max(p1.getLat(), p3.getLat())) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }
        return intersectCount % 2 != 0;
    }

    /**
     * 计算两点之间的距离
     *
     * @param point1 点1
     * @param point2 点2
     * @return 两点之间的距离，单位为米
     */
    public static double getDistance(Point point1, Point point2) {
        if (point1 == null || point2 == null) {
            return 0;
        }
        double lng1 = degreeToRad(point1.getLng());
        double lat1 = degreeToRad(point1.getLat());
        double lng2 = degreeToRad(point2.getLng());
        double lat2 = degreeToRad(point2.getLat());
        return EARTH_RADIUS * Math.acos(
                Math.sin(lat1) * Math.sin(lat2) +
                        Math.cos(lat1) * Math.cos(lat2) * Math.cos(lng2 - lng1)
        );
    }

    /**
     * 将度转化为弧度
     *
     * @param degree 度
     * @return 弧度
     */
    public static double degreeToRad(double degree) {
        return Math.PI * degree / 180;
    }

    /**
     * 将弧度转化为度
     *
     * @param rad 弧度
     * @return 度
     */
    public static double radToDegree(double rad) {
        return 180 * rad / Math.PI;
    }

    /**
     * 计算折线或点数组的长度
     *
     * @param polyline 折线对象或点数组
     * @return 折线或点数组的长度
     */
    public static double getPolylineDistance(List<Point> polyline) {
        if (polyline == null || polyline.size() < 2) {
            return 0;
        }
        double totalDis = 0;
        for (int i = 0; i < polyline.size() - 1; i++) {
            Point curPt = polyline.get(i);
            Point nextPt = polyline.get(i + 1);
            double dis = getDistance(curPt, nextPt);
            totalDis += dis;
        }
        return totalDis;
    }

    /**
     * 计算多边形或点数组的面积
     *
     * @param polygon 多边形对象或点数组
     * @return 多边形或点数组的面积
     */
    public static double getPolygonArea(List<Point> polygon) {
        if (polygon == null || polygon.size() < 3) {
            return 0;
        }
        double totalArea = 0;
        int count = polygon.size();
        for (int i = 0; i < count; i++) {
            Point p1 = polygon.get(i);
            Point p2 = polygon.get((i + 1) % count);
            totalArea += (p1.getLng() * p2.getLat() - p2.getLng() * p1.getLat());
        }
        return Math.abs(totalArea / 2);
    }

    public static void main(String[] args) {
        Point point = new Point(116.404, 39.915);
        Point sw = new Point(116.0, 39.0);
        Point ne = new Point(117.0, 40.0);
        Bounds bounds = new Bounds(sw, ne);

        boolean isInRect = GeoUtils.isPointInRect(point, bounds);
        System.out.println("点是否在矩形内: " + isInRect);
    }

    /**
     * 1.矩形数据为：{sw: {lng: 116.392502, lat: 39.905536}, ne: {lng: 116.41787, lat: 39.915111}}
     * 2.多边形数据为：[{"lng":116.393867,"lat":39.921087},{"lng":116.389915,"lat":39.924739},{"lng":116.386609,"lat":39.9207},{"lng":116.396167,"lat":39.916162},{"lng":116.396454,"lat":39.916273}]
     * 3.圆形数据为：{center: {lng: 116.387759, lat: 39.922249}, radius: 484.0900996086308}
     * 4.当前经纬度参数为：{lng: 116.387759, lat: 39.922249}
     * 5.根据地理围栏类型判断：是圆形还是多边形、矩形，判断经纬度是否在围栏内
     * */
    public static boolean isPointInGeofence(String lat, String lng, String geofenceType, String geofenceData) {
        if (lat == null || lng == null || geofenceType == null || geofenceData == null) {
            return false;
        }

        Point point = new Point(Double.parseDouble(lng), Double.parseDouble(lat));

        switch (geofenceType) {
            case "2":
                Bounds bounds = parseBounds(geofenceData);
                return isPointInRect(point, bounds);
            case "3":
                Polygon polygon = parsePolygon(geofenceData);
                return isPointInPolygon(point, polygon);
            case "1":
                Circle circle = parseCircle(geofenceData);
                return isPointInCircle(point, circle);
            default:
                throw new IllegalArgumentException("Unsupported geofence type: " + geofenceType);
        }
    }

    /**
     * 将字符串转换为 Bounds 对象（使用 Gson）
     *
     * @param boundsStr 字符串形式的 Bounds 数据
     * @return Bounds 对象
     */
    public static Bounds parseBounds(String boundsStr) {
        // 使用 Gson 解析 JSON 字符串
        Gson gson = new Gson();
        return gson.fromJson(boundsStr, Bounds.class);
    }

    /**
     * 将字符串转换为 Polygon 对象
     *
     * @param polygonStr 字符串形式的 Polygon 数据
     * @return Polygon 对象
     */
    public static Polygon parsePolygon(String polygonStr) {
        // 使用 Gson 解析 JSON 字符串
        Gson gson = new Gson();
        Type pointListType = new TypeToken<List<Point>>() {}.getType();
        List<Point> points = gson.fromJson(polygonStr, pointListType);
        return new Polygon(points);
    }


    /**
     * 将字符串转换为 Circle 对象
     *
     * @param circleStr 字符串形式的 Circle 数据
     * @return Circle 对象
     */
    public static Circle parseCircle(String circleStr) {
        // 使用 Gson 解析 JSON 字符串
        Gson gson = new Gson();
        return gson.fromJson(circleStr, Circle.class);
    }


}
